#include "File/FileInventory.h"
#include "Functions/TerminalIO.h"
#include "File/SystemPath.h"
#include "TError.h"
#include "TFile.h"
#include "TList.h"
#include "TSystem.h"
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
using namespace BOSS_Afterburner::TerminalIO;

FileInventory::FileInventory(const TString& path, bool recursive)
{
  AddFiles(path, recursive);
}

void FileInventory::AddFiles(const TString& path, bool recursive)
{
  SystemPath file(path.Data());
  if(file.IsEmpty())
  {
    PrintWarning(Form("Cannot load path \"%s\"", path.Data()));
    return;
  }
  if(recursive && file.IsDirectory())
  {
    TIter it(file.GetFiles());
    while(auto obj = it.Next())
    {
      if(!IsValidDir(obj->GetName())) continue;
      AddFilesRecursive(Form("%s/%s", file.GetPath(), obj->GetName()));
    }
  }
  else
    AddFile(file.GetPath());
}

void FileInventory::AddFile(const TString& path)
{
  auto store        = gErrorIgnoreLevel;
  gErrorIgnoreLevel = 6000;
  TFile file(path.Data());
  if(file.IsZombie())
    fZombies.insert(path);
  else
    fFiles.insert(path);
  gErrorIgnoreLevel = store;
}

bool FileInventory::IsValidDir(const TString& path)
{
  if(path.EqualTo(".")) return false;
  if(path.EqualTo("..")) return false;
  return true;
}

void FileInventory::Print() const
{
  if(IsEmpty())
    cout << "Empty file inventory" << endl;
  else
    for(const auto& file : fFiles)
      cout << file << endl;
}

void FileInventory::PrintZombies() const
{
  for(const auto& file : fZombies)
    cout << file << endl;
}

void FileInventory::RemoveZombies()
{
  size_t count = 0;
  for(const auto& file : fZombies)
    if(file.EndsWith(".root")) ++count;
  if(!count)
  {
    cout << "No zombie ROOT files to remove." << endl;
    return;
  }
  TString input;
  cout << "Remove " << fZombies.size() << " zombie files? (y/n) ";
  cin >> input;
  input.ToLower();
  if(!input.EqualTo("y") && !input.EqualTo("yes")) return;
  set<TString> newzombies;
  for(const auto& file : fZombies)
  {
    if(file.EndsWith(".root"))
    {
      cout << "Removing \"" << file << "\"" << endl;
      gSystem->Exec(Form("rm %s", file.Data()));
    }
    else
      newzombies.insert(file);
  }
  fZombies = newzombies;
}

TString FileInventory::First() const
{
  if(IsEmpty())
    return "";
  else
    return *fFiles.begin();
}

TString FileInventory::GetCommonPWD() const
{
  if(IsEmpty()) return "";
  string common = First().Data();
  for(const auto& file : fFiles)
  {
    string output;
    string s = file.Data();
    set_intersection(common.begin(), common.end(), s.begin(), s.end(), back_inserter(output));
    common = output;
  }
  return common.c_str();
}